/**
 * Title: jFamilyTree provider
 * Description: jFamilyTree Provider
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 */
package jFamilyTree.provider.xml.fast;

import java.util.Enumeration;
import java.util.Hashtable;

/**
 * HTML tag
 * 
 * @author Bernard Bou
 */
class Tag
{
	/**
	 * Tag name
	 */
	public String theName;

	/**
	 * Attributes
	 */
	public Hashtable<String, String> theAttributes;

	/**
	 * Whether tag is <xxx> or <xxx/>
	 */
	public boolean open = false;

	/**
	 * Whether tag is <xxx/>
	 */
	public boolean standalone = false;

	/**
	 * Whether tag is <xxx>
	 */
	public boolean unclosed = false;

	/**
	 * Constructor
	 * 
	 * @param thisName
	 *        tag name
	 */
	protected Tag(String thisName)
	{
		theName = thisName;
	}

	/**
	 * Make tag from string
	 * 
	 * @param thisTagString
	 *        tag string
	 * @return tag
	 */
	static public Tag makeTag(String thisTagString)
	{
		// in assert : thatString is string between leading '<' and trailing '>'

		// null tag
		if (thisTagString == null || thisTagString.isEmpty())
			return null;

		// comment special case
		if (thisTagString.charAt(0) == '!')
			return new Tag("!");
		if (thisTagString.charAt(0) == '?')
			return new Tag("!");

		// create tag
		Tag thisTag = new Tag("#");

		// [p-q] : valid string
		int p = 0;
		int q = thisTagString.length() - 1;

		// flags and strip leading and trailing /
		if (thisTagString.charAt(0) == '/')
		{
			// starts with /
			p++;
		}
		else
		{
			thisTag.unclosed = true;
			if (thisTagString.charAt(q) == '/')
			{
				// ends with /
				q--;
				thisTag.standalone = true;
			}
		}
		thisTag.open = thisTag.unclosed || thisTag.standalone;

		// trim spaces
		char c;
		while (p <= q && ((c = thisTagString.charAt(p)) == ' ' || c == '\t'))
		{
			p++;
		}
		while (p <= q && ((c = thisTagString.charAt(q)) == ' ' || c == '\t'))
		{
			q--;

			// extract tagname
		}
		int q3 = p;
		while (q3 <= q && (c = thisTagString.charAt(q3)) != ' ' && c != '\t')
		{
			q3++;
		}
		thisTag.theName = thisTagString.substring(p, q3).toLowerCase();

		// attribute [p2-q]
		int r;
		for (int p2 = p; p2 < q && (r = thisTagString.indexOf("=", p2)) != -1; p2 = r + 1)
		{
			// ATTRIBUTE NAME
			String thisAttributeName = null;

			// attribute name [p3-q3] or [p3-(q3+1)[
			q3 = r - 1;
			// back skip blanks to first non space : q3
			while (q3 >= p2 && ((c = thisTagString.charAt(q3)) == ' ' || c == '\t'))
			{
				q3--;
				// back skip to start of name : p3
			}
			int p3 = q3;
			while (p3 >= p2 && (c = thisTagString.charAt(p3)) != ' ' && c != '\t')
			{
				p3--;
				// extract name
			}
			thisAttributeName = thisTagString.substring(p3 + 1, q3 + 1).toLowerCase();

			// ATTRIBUTE VALUE
			p3 = r + 1;
			// forward skip blanks to first non space : p3
			while (p3 <= q && ((c = thisTagString.charAt(p3)) == ' ' || c == '\t'))
			{
				p3++;

			}
			String thisAttributeValue = null;
			if (thisTagString.charAt(p3) == ('"'))
			{
				// if starts with inverted commas, cut at next inverted comma
				q3 = thisTagString.indexOf("\"", ++p3);
				if (q3 != -1)
				{
					thisAttributeValue = thisTagString.substring(p3, q3);
				}
			}
			else
			{
				// else cut at first blank
				q3 = p3;
				while (q3 >= q && (c = thisTagString.charAt(q3)) != ' ' && c != '\t')
				{
					q3++;
				}
				thisAttributeValue = thisTagString.substring(p3, q3);
			}
			if (thisAttributeName != null && thisAttributeValue != null)
			{
				if (thisTag.theAttributes == null)
					thisTag.theAttributes = new Hashtable<String, String>();
				thisTag.theAttributes.put(thisAttributeName, thisAttributeValue);
			}
		}
		return thisTag;
	}

	/**
	 * Get attribute
	 * 
	 * @param thisName
	 *        name
	 * @return value
	 */
	public String getAttribute(String thisName)
	{
		return theAttributes == null ? null : theAttributes.get(thisName);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	public String toString()
	{
		StringBuffer thisResult = new StringBuffer();
		if (!unclosed)
		{
			thisResult.append("</");
		}
		else
		{
			thisResult.append("<");
		}
		thisResult.append(theName);
		if (theAttributes != null)
		{
			for (Enumeration<String> theseAttributes = theAttributes.keys(); theseAttributes.hasMoreElements();)
			{
				String thisKey = theseAttributes.nextElement();
				thisResult.append(" ");
				thisResult.append(thisKey);
				thisResult.append("=\"");
				thisResult.append(theAttributes.get(thisKey));
				thisResult.append("\"");
			}
		}
		if (standalone)
		{
			thisResult.append("/>");
		}
		else
		{
			thisResult.append(">");
		}
		return thisResult.toString();
	}
}